package thimeeeee.Control;

import java.awt.event.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import thimeeeee.Model.*;
//import thimeeeee.Skeleton;
import thimeeeee.View.View;

public class GameController extends WindowAdapter implements ActionListener, KeyListener, WindowListener
{
    //a timer azonosítására használt szám
    long tID;
    Timer t;
    boolean timerRunning = false;
	private Game game;
    private Player player1;
    private Player player2;
    private double time = 1;

    //a proto teszteléséhez szükséges referencia
    private Beast beast;

    private View view;


    public GameController()
    {
        //konzolos működésre írt konstruktor
//        Skeleton.Create("GameController");
//        Skeleton.StartFunc("GameController()");

        view = new View(this);

        String s = new String();

        while (!s.equalsIgnoreCase("x"))
        {
            System.out.println("Adja meg a futtatni kívánt teszteset számát!(Kilépés: x)");

            //beolvasás a konzolról
            try
            {
                InputStreamReader isr = new InputStreamReader(System.in);
                BufferedReader br = new BufferedReader(isr);
                s = br.readLine();

                //ha nem a kilépést választottuk, akkor a teszteset futtatása
                if (!s.equalsIgnoreCase("x"))
                {
                    //a teszteset kiválasztása a beolvasott érték alapján
                    //ha létezik az adott számú térkép, akkor betöltjük azt
                    File f = new File("maps/" + s + ".map");
                    if (f.exists())
                    {
                        //az eset számának számmá konvertálása -> a térkép betöltése a sorszám megadásával történik
                        int eset = Integer.parseInt(s);
                        //adott pálya betöltése
                        Start(eset);

                        //ha kiválasztottuk valamelyik esetet, és létezik a hozzá tartozó utasításállomány,
                        //akkor a teszteset parancsainak betöltése

                        //a protoban kiadott parancsokat már nem futtatjuk
                        //runTest(eset);
                    }
                }
            }
            catch(NumberFormatException ex)
            {
                System.out.println("A megadott sorszámú térkép, vagy teszteset nem létezik!\n" + ex);
            }
            catch (Exception ex)
            {
                System.out.println("Input hiba!");
            }
        } 
    }

    public GameController(String s)
    {
        //ha létezik az adott számú térkép, akkor betöltjük azt
        File f = new File("maps/" + s + ".map");
        if (f.exists())
        {
            //az eset számának számmá konvertálása -> a térkép betöltése a sorszám megadásával történik
            int eset = Integer.parseInt(s);
            //adott pálya betöltése
            Start(eset);

            //ha kiválasztottuk valamelyik esetet, és létezik a hozzá tartozó utasításállomány,
            //akkor a teszteset parancsainak betöltése
            runTest(eset);
        }
    }
//    public GameController(String s){
//        //argumentummal történő indításra írt konstruktor
//
//        Skeleton.Create("GameController");
//        Skeleton.StartFunc("GameController()");
//        //game = new Game();
//
//        //a protohoz felhasznált változók
//        //a beolvasott szöveg(teszteset száma, utasítás, ...)
//        //a futtatott teszteset száma
//        try
//        {
//                InputStreamReader isr = new InputStreamReader(System.in);
//                BufferedReader br = new BufferedReader(isr);
//                s = br.readLine();
//        }
//        catch (Exception ex)
//        {
//                System.out.println("Input hiba!");
//        }
//
//            //ha nem a kilépést választottuk, akkor a teszteset futtatása
//            if (!s.equalsIgnoreCase("x")){
//
//                //a teszteset kiválasztása a beolvasott érték alapján
//                //ha létezik az adott számú térkép, akkor betöltjük azt
//                File f = new File("maps/" + s + ".map");
//                if (f.exists())
//                {
//                    //az eset számának számmá konvertálása -> a térkép betöltése a sorszám megadásával történik
//                    try
//                    {
//                        int eset = Integer.parseInt(s);
//                        //adott pálya betöltése
//                        game = new Game(eset);
//
//                        //ha kiválasztottuk valamelyik esetet, és létezik a hozzá tartozó utasításállomány,
//                        //akkor a teszteset parancsainak betöltése
//                        f = new File("maps/" + s +".in");
//                        if ((eset > 0)&&(f.exists())){
//                            runTest(eset);
//                        }
//                    }
//                    catch(Exception ex)
//                    {
//                        System.out.println("A megadott sorszámú térkép, vagy teszteset nem létezik!\n" + ex);
//                    }
//                }
//            }
//
//
//
//
//        //Skeleton.EndFunc("GameController()", "void");
//    }

    //adott számú teszteset utasításainak beolvasása, majd futtatása
    public void runTest(int eset)
    {
        String command = new String();

        try 
        {
            File f = new File("maps/" + String.valueOf(eset)+".in");

            if(!f.exists())
            {
                throw new java.io.FileNotFoundException("A szükséges bemeneti fájl nem található!");
            }
            else
            {
                BufferedReader input =  new BufferedReader(new FileReader(f));

                while((command = input.readLine()) != null)
                {
                    runCommand(command);
                }
                //az eredmény kiírása
                //a térkép lekérése a game-től
                char[][] map = game.GetStringMap();

                int lives = 0, bombCount = 0, diamonds = 0;
                GameInfo gi = game.GetGameInfo();
                if(gi != null)
                {
                    lives = gi.GetLives();
                    diamonds = gi.GetRequiredDiamonds() - gi.GetDiamonds();
                }

                if(player2 == null)
                {
                    bombCount = player1.GetCountBomb();
                }
                else if(player1 == null)
                {
                    bombCount = player2.GetCountBomb();
                }
                else
                {
                    bombCount = player1.GetCountBomb();
                }
                
                ResultToFile(eset, map, lives, bombCount, diamonds);
            }
        }
        catch(Exception ex)
        {
            System.out.println("Error: " + ex);
        }
    }

    private void ResultToFile(int eset, char[][] map, int szint, int bomb, int diamond)
    {
        String outputFileName = String.valueOf(eset)+".out";
        try
        {
            FileWriter outputFileReader = new FileWriter(outputFileName);
            PrintWriter outputStream  = new PrintWriter(outputFileReader);
            outputStream.println(String.valueOf(szint) + " " +
                                 String.valueOf(diamond) + " " +
                                 String.valueOf(bomb));

            for(int y = 0; y < map.length; y++)
            {
                for(int x = 0; x < map[y].length; x++)
                {
                    if(x == 0 && y%2 == 1)
                    {
                        outputStream.print("   ");
                    }
                    //space-ek szűrése
                    if (map[y][x] != ' ')
                    {
                        outputStream.print(map[y][x]);
                        outputStream.print("     ");
                    }
                }
                outputStream.println();
            }

            outputStream.close();
        }

        catch (Exception ex)
        {
            System.out.println("Error: " + ex);
        }
    }

    //teszteset utasításainak végrehajtása
    //a kapott parancsot futtatja
    public void runCommand(String comm)
    {
        System.out.println(comm + " FUT");
        

        if (comm.compareToIgnoreCase("idle") == 0){
            //idle hívás, majd az idő növelése
            Idle(time++);
        }
        
        //player1 irányítása
        if (comm.compareToIgnoreCase("k_move_up") == 0){
            player1.SetDirection(Direction.up);
        }
        if (comm.compareToIgnoreCase("k_move_down") == 0){
            player1.SetDirection(Direction.down);
        }
        if (comm.compareToIgnoreCase("k_move_upleft") == 0){
            player1.SetDirection(Direction.upleft);
        }
        if (comm.compareToIgnoreCase("k_move_upright") == 0){
            player1.SetDirection(Direction.upright);
        }
        if (comm.compareToIgnoreCase("k_move_downleft") == 0){
            player1.SetDirection(Direction.downleft);
        }
        if (comm.compareToIgnoreCase("k_move_downright") == 0){
            player1.SetDirection(Direction.downright);
        }
        if (comm.compareToIgnoreCase("k_put_bomb") == 0){
            //bomba letétele
            player1.PlaceBomb();
        }

        //player2 irányítása
        if (comm.compareToIgnoreCase("l_move_up") == 0){
            player2.SetDirection(Direction.up);
        }
        if (comm.compareToIgnoreCase("l_move_down") == 0){
            player2.SetDirection(Direction.down);
        }
        if (comm.compareToIgnoreCase("l_move_upleft") == 0){
            player2.SetDirection(Direction.upleft);
        }
        if (comm.compareToIgnoreCase("l_move_upright") == 0){
            player2.SetDirection(Direction.upright);
        }
        if (comm.compareToIgnoreCase("l_move_downleft") == 0){
            player2.SetDirection(Direction.downleft);
        }
        if (comm.compareToIgnoreCase("l_move_downright") == 0){
            player2.SetDirection(Direction.downright);
        }
        if (comm.compareToIgnoreCase("l_put_bomb") == 0){
            //bomba letétele
            player2.PlaceBomb();
        }

        //a beastek mozgatása a <<típus sorszáma>>_utasítás formában történik, így az első karakter a típust adja
        char firstChar = comm.charAt(0);

        //ha számmal kezdődik, akkor a megfelelő típus bekérése a game-től, valamint az utasítás végrehajtása rajta
        int beastNum = Character.getNumericValue(firstChar);
        if ((beastNum > -1) && (beastNum < 8))
        {
            switch (beastNum)
            {
                case 0:
                    beast = game.b0;
                    break;
                case 1:
                    beast = game.b1;
                    break;
                case 2:
                    beast = game.b2;
                    break;
                case 3:
                    beast = game.b3;
                    break;
                case 4:
                    beast = game.b4;
                    break;
                case 5:
                    beast = game.b5;
                    break;
                case 6:
                    beast = game.b6;
                    break;
                case 7:
                    beast = game.b7;
                    break;
            }

            //ha megvan a referencia, akkor az utasítás végrehajtása

            //az utasítás beast-fajtától független részének kiszűrése
            comm = comm.substring(1);

            if (comm.compareToIgnoreCase("_move_up") == 0){
                beast.SetDirection(Direction.up);
            }
            if (comm.compareToIgnoreCase("_move_down") == 0){
                beast.SetDirection(Direction.down);
            }
            if (comm.compareToIgnoreCase("_move_upleft") == 0){
                beast.SetDirection(Direction.upleft);
            }
            if (comm.compareToIgnoreCase("_move_upright") == 0){
                beast.SetDirection(Direction.upright);
            }
            if (comm.compareToIgnoreCase("_move_downleft") == 0){
                beast.SetDirection(Direction.downleft);
            }
            if (comm.compareToIgnoreCase("_move_downright") == 0){
                beast.SetDirection(Direction.downright);
            }
        }
    }



    public void Idle(double aTimeStamp){
//        Skeleton.StartFunc("GameController.Idle()");

        game.Idle(aTimeStamp);
        //view.paint(game.GetStringMap(), game.GetGameInfo());

//        Skeleton.EndFunc("GameController.Idle()", "void");
    }

//Grafikus

    //a játékos vesztett-->játék vége
    public void GameEnd(){
        timerRunning = false;
        //játék végének jelzése
        view.ChangePanel(thimeeeee.View.MenuState.PlayerDead);
    }

    //az inicializálás sok helyen szerepel, úgyhogy külön vettem
    //itt állítjuk be a playerek referenciáját, úgyhogy új pálya kezdésekor is ezt kell meghívni
    public void Start(int lvl){
        game = new Game(lvl, this);
        
        player1 = game.GetPlayer(1);
        player2 = game.GetPlayer(2);

        timerRunning = true;
        t = new Timer();
        tID = t.getId();
        t.start();
    }

    //restartnál az életek száma nem a beolvasott, hanem a paraméterként kapott lesz
    public void Start(int lvl, int lives){
        game = new Game(lvl, this);

        player1 = game.GetPlayer(1);
        player2 = game.GetPlayer(2);

        game.GetGameInfo().SetLives(lives);

        timerRunning = true;
        t = new Timer();
        tID = t.getId();
        t.start();
    }

    //gombok eseménykezelője
    public void actionPerformed(ActionEvent ae)
    {
        if (ae.getActionCommand().equalsIgnoreCase("jatek"))
        {
            //játék paneljére váltás
            view.ChangePanel(thimeeeee.View.MenuState.Game);
            //1. pálya betöltése
            Start(1);
        }
        else if (ae.getActionCommand().equalsIgnoreCase("fomenu"))
        {
            view.ChangePanel(thimeeeee.View.MenuState.StartUp);
        }
        else if (ae.getActionCommand().equalsIgnoreCase("about"))
        {
            view.ChangePanel(thimeeeee.View.MenuState.About);
        }
        else if (ae.getActionCommand().equalsIgnoreCase("instructions"))
        {
            view.ChangePanel(thimeeeee.View.MenuState.Instructions);
        }
        else if (ae.getActionCommand().equalsIgnoreCase("restart"))
        {
            //újrakezdés, ha erre a restart gombra kattintunk
            view.ChangePanel(thimeeeee.View.MenuState.Game);
            Start(1);
        }
        else if(ae.getActionCommand().equalsIgnoreCase("kill"))
        {
            this.game.KillPlayer();
        }
    }

    //billentyűleütés
    public void keyPressed(KeyEvent ke){
        switch (ke.getKeyChar())
        {
            //player1
            case 'a':
                if (player1 != null)
                    player1.SetDirection(Direction.downleft);
                break;
            case 's':
                if (player1 != null)
                    player1.SetDirection(Direction.down);
                break;
            case 'd':
                if (player1 != null)
                    player1.SetDirection(Direction.downright);
                break;
            case 'q':
                if (player1 != null)
                    player1.SetDirection(Direction.upleft);
                break;
            case 'w':
                if (player1 != null)
                    player1.SetDirection(Direction.up);
                break;
            case 'e':
                if (player1 != null)
                    player1.SetDirection(Direction.upright);
                break;
            case 'c':
                if (player1 != null)
                    player1.PlaceBomb();
                break;

            //player2
            case 'j':
                if (player2 != null)
                    player2.SetDirection(Direction.downleft);
                break;
            case 'k':
                if (player2 != null)
                    player2.SetDirection(Direction.down);
                break;
            case 'l':
                if (player2 != null)
                    player2.SetDirection(Direction.downright);
                break;
            case 'u':
                if (player2 != null)
                    player2.SetDirection(Direction.upleft);
                break;
            case 'i':
                if (player2 != null)
                    player2.SetDirection(Direction.up);
                break;
            case 'o':
                if (player2 != null)
                    player2.SetDirection(Direction.upright);
                break;
            case 'n':
                if (player2 != null)
                   player2.PlaceBomb();
                break;
        }
    }

    @Override
     public void windowClosing(WindowEvent we) { System.exit(0); }
     // Ezekre a metódusokra nincs szükségem
	 public void keyReleased(KeyEvent ke) { }
	 public void keyTyped(KeyEvent ke) { }



    //időzítő osztály
    public class Timer extends Thread{
        @Override
        public void run()
        {
            while (timerRunning && this.getId() == tID)
            {
                Idle();
                
                try
                {
                    sleep(400);
                }
                catch(InterruptedException ex) { }
            }
        }
    }

    //végleges idle, ami a saját time változót használja
    public void Idle()
    {
        game.Idle(time++);
        view.paint(game.GetStringMap(), game.GetGameInfo().GetLives(), game.GetLevel().GetBombCount(),
                game.GetGameInfo().GetRequiredDiamonds(), game.GetGameInfo().GetDiamonds(),
                game.GetLevel().GetMaxWidth(), game.GetLevel().GetMaxHeight());
    }
}